﻿#include "..\..\include\Server\CMyServer.h"

//初始化界面
void CMyServer::InitUI()
{
	//构造一个WSADATA结构的指针
	WSADATA wsaData;

	//调用WSAStartup()初始化Winsock库，这里我们统一使用Winsock2的子版本，2.2版。用MAKEWORD宏来构造版本号。
	int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (nResult != 0)
	{
		//调用错误的情况
		return;
	}

	//判断加载的版本号是否正常
	if (HIBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2)
	{
		return;
	}

    ///////////////////////////////////////////

    //获取本地主机名
    char szHostp[2048] = {0};
    ::gethostname(szHostp, 2048);

    //通过机器名获取所有IP地址，返回一个hostent结构指针
	hostent *pHostent = ::gethostbyname(szHostp);
	if (pHostent == NULL)
	{
		return;
	}

	//循环遍历并打印出所有的IP地址
	SendDlgItemMessageA(this->m_hWnd, IDC_SERVER_COMBO, CB_ADDSTRING, NULL, (LPARAM)"绑定所有IP地址");
	SendDlgItemMessageA(this->m_hWnd, IDC_SERVER_COMBO, CB_ADDSTRING, NULL, (LPARAM)"127.0.0.1");

	in_addr addr;
	for (int i = 0; pHostent->h_addr_list[i] != 0; ++i)
	{
		//h_addr_list储存了IP地址列表
		char *p = pHostent->h_addr_list[i];

		//检测是否返回空指针
		if (p == NULL)
		{
			break;
		}

		//把指向h_addr_list元素的p指针复制给in_addr结构
		memcpy((void*)&addr, p, pHostent->h_length);

		//用inet_ntoa()函数把32位的IP地址转换为字符串
		char *szIp = ::inet_ntoa(addr);

		//添加IP地址到组合框列表
		SendDlgItemMessageA(this->m_hWnd, IDC_SERVER_COMBO, CB_ADDSTRING, NULL, (LPARAM)szIp);
	}

	//////////////////////////////////////////////////////////////////////////

	//设置默认端口号
	SendDlgItemMessage(this->m_hWnd, IDC_PORT_EDIT, WM_SETTEXT, NULL, (LPARAM)_T("9527"));
	//设置IP列表默认项
	SendDlgItemMessage(this->m_hWnd, IDC_SERVER_COMBO, CB_SETCURSEL, 0, 0);
	//勾选自动保存选项
	SendDlgItemMessage(this->m_hWnd, IDC_AUTOSAVE_CHECK, BM_SETCHECK, TRUE, 0);
	//初始化在线列表
	HWND hOnlineList = GetDlgItem(this->m_hWnd, IDC_ONLINE_LIST);
	long lStyle = GetWindowLong(hOnlineList, GWL_STYLE);          //获取当前窗口style
	lStyle &= ~LVS_TYPEMASK;                                      //清除显示方式位
	lStyle |= LVS_REPORT;                                         //报表方式
	SetWindowLong(hOnlineList, GWL_STYLE, lStyle);                //设置style

	long dwStyle = SendMessage(hOnlineList, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);

	dwStyle |= LVS_EX_FULLROWSELECT;                //选中某行使整行高亮（只适用与report风格的listctrl）
	dwStyle |= LVS_EX_GRIDLINES;                    //网格线（只适用与report风格的listctrl）
	dwStyle |= LVS_EX_SUBITEMIMAGES;                //添加图标

	//设置扩展风格
	SendMessage(hOnlineList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (LPARAM)dwStyle);

	//添加列头
	LVCOLUMN cn;
	cn.mask = LVCF_WIDTH | LVCF_TEXT;
	cn.fmt = LVCFMT_RIGHT;
	cn.cx = 85;
	cn.pszText = _T("用户名");
	SendDlgItemMessage(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 1, (LPARAM)&cn);

	cn.cx = 110;
	cn.pszText = _T("登录时间");
	SendDlgItemMessage(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 2, (LPARAM)&cn);

	cn.cx = 120;
	cn.pszText = _T("IP地址");
	SendDlgItemMessage(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 3, (LPARAM)&cn);

/*
	cn.cx = 150;
	cn.pszText = _T("登录地区");
	SendDlgItemMessage(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 4, (LPARAM)&cn);*/

	cn.cx = 90;
	cn.pszText = _T("心跳时间(s)");
	SendDlgItemMessage(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 5, (LPARAM)&cn);

	cn.cx = 150;
	cn.pszText = _T("通信密钥");
	SendDlgItemMessage(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTCOLUMN, 6, (LPARAM)&cn);
}

//启动服务器
BOOL CMyServer::StartServer()
{
	AddLog("正在启动服务器...");
	int nSel = SendDlgItemMessage(this->m_hWnd, IDC_SERVER_COMBO, CB_GETCURSEL, 0, 0);
	if (nSel == 0)
	{
		//获取端口号
		char szPort[6] = {0};
		GetDlgItemTextA(this->m_hWnd, IDC_PORT_EDIT, szPort, 6);
		debug.Trace("端口：%s\n", szPort);
		if (this->server.Start(ADDR_ANY, atoi(szPort), this->m_hWnd) == FALSE)
		{
			AddLog("服务器启动失败！错误：%d", ::WSAGetLastError());
			::MessageBox(this->m_hWnd, _T("启动服务器失败！"), _T("错误"), MB_ICONSTOP);
			return FALSE;
		}
	}
	else
	{
		//获取IP地址
		char szIP[16] = {0};
		char szPort[6] = {0};
		GetDlgItemTextA(this->m_hWnd, IDC_SERVER_COMBO, szIP, 16);
		GetDlgItemTextA(this->m_hWnd, IDC_PORT_EDIT, szPort, 6);
		debug.Trace("获取到的IP地址：%s，端口：%s\n", szIP, szPort);

		if (this->server.Start(inet_addr(szIP), atoi(szPort), this->m_hWnd) == FALSE)
		{
			AddLog("服务器启动失败！错误：%d", ::WSAGetLastError());
			::MessageBox(this->m_hWnd, _T("启动服务器失败！"), _T("错误"), MB_ICONSTOP);
			return FALSE;
		}
	}

	//连接数据库

	AddLog("正在连接数据库...");
	
	try
	{
		m_DbConn.Connect(DEFAULT_DB_SERVER, DEFAULT_DB_USER, DEFAULT_DB_PASS, SA_SQLServer_Client);
		if (m_DbConn.isConnected() == true)
		{
			//AddLog("连接数据库成功。");
		}
		else
		{
			AddLog("连接到数据库失败。");
			this->server.Stop();
			return FALSE;
		}
	}
	catch(SAException &x)
	{
		try
		{
			m_DbConn.Rollback();
		}
		catch(SAException &)
		{
		}
		AddLog("连接数据库发生错误。%s", (const char*)x.ErrText());
		this->server.Stop();
		return FALSE;
	}

	//加载IP数据库
/*
	AddLog("正在加载IP数据库...");
	if (m_Locator.LoadLib("qqwry.dat") == false)
	{
		AddLog("加载IP数据库失败。");
		this->server.Stop();
		return FALSE;
	}
	AddLog("加载IP数据库完成！");*/
	
	//启动时钟
	AddLog("启动服务器时钟...");
	::SetTimer(this->m_hWnd, IDT_CHECK_TIME, 1000, (TIMERPROC)Heart_CheckProc);		//心跳包检测时钟
	::SetTimer(this->m_hWnd, IDT_DEAL_TIME, 1000, (TIMERPROC)Heart_DealTimeProc);	//客户端心跳包倒计时时钟

	AddLog("服务器启动成功，正在等待客户机接入...");
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_START_BUTTON), FALSE);
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_STOP_BUTTON), TRUE);
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_SERVER_COMBO), FALSE);
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_PORT_EDIT), FALSE);
	return TRUE;
}

//停止服务器
void CMyServer::StopServer()
{
	//关闭服务器
	this->server.Stop();
	this->UpdateOnlineList();
	//关闭数据库
	m_DbConn.Disconnect();
	m_DbConn.Rollback();
	//关闭服务器时钟
	::KillTimer(this->m_hWnd, IDT_CHECK_TIME);
	::KillTimer(this->m_hWnd, IDT_DEAL_TIME);

	AddLog("服务器成功停止。");

	EnableWindow(GetDlgItem(this->m_hWnd, IDC_START_BUTTON), TRUE);
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_STOP_BUTTON), FALSE);
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_SERVER_COMBO), TRUE);
	EnableWindow(GetDlgItem(this->m_hWnd, IDC_PORT_EDIT), TRUE);
}

//添加日志
void CMyServer::AddLog(LPCSTR format, ...)
{
	va_list vl;
	va_start(vl, format);

	char buf[2048] = {0};
	char buffer[2048] = {0};
	char timebuf[20] = {0};
	

	//获取时间
	SYSTEMTIME time;
	GetLocalTime(&time);
	sprintf_s(timebuf, "[%u:%u:%u] ", time.wHour, time.wMinute, time.wSecond);

	//格式化
	vsprintf_s(buf, format, vl);
	sprintf_s(buffer, "%s%s", timebuf, buf);
	va_end(vl);

	SendDlgItemMessageA(this->m_hWnd, IDC_LOGS_LIST, LB_ADDSTRING, NULL, (LPARAM)buffer);

	//选中日志列表最后一项
	int nCount = SendDlgItemMessage(this->m_hWnd, IDC_LOGS_LIST, LB_GETCOUNT, NULL, NULL);
	SendDlgItemMessage(this->m_hWnd, IDC_LOGS_LIST, LB_SETCURSEL, nCount - 1, 0); 
}

void CMyServer::AddLog(LPCTSTR format, ...)
{
	va_list vl;
	va_start(vl, format);

	wchar_t buf[2048] = {0};
	wchar_t timebuf[20] = {0};
	wchar_t buffer[2048] = {0};

	//获取时间
	SYSTEMTIME time;
	GetLocalTime(&time);

	wsprintf(timebuf, _T("[%u:%u:%u] "), time.wHour, time.wMinute, time.wSecond);
	wvsprintf(buf, format, vl);
	wsprintf(buffer, _T("%s%s"), timebuf, buf);
	va_end(vl);

	SendDlgItemMessage(this->m_hWnd, IDC_LOGS_LIST, LB_ADDSTRING, NULL, (LPARAM)buffer);

	//选中日志列表最后一项
	int nCount = SendDlgItemMessage(this->m_hWnd, IDC_LOGS_LIST, LB_GETCOUNT, NULL, NULL);
	SendDlgItemMessage(this->m_hWnd, IDC_LOGS_LIST, LB_SETCURSEL, nCount - 1, 0); 
}

BOOL CMyServer::InitCom()
{
	//初始化公共控件
    INITCOMMONCONTROLSEX InitCtrls;
    InitCtrls.dwICC = ICC_LINK_CLASS | ICC_WIN95_CLASSES;
    InitCtrls.dwSize = sizeof(InitCtrls);
    return ::InitCommonControlsEx(&InitCtrls);
}

void CMyServer::ClearLog()
{
	SendDlgItemMessage(this->m_hWnd, IDC_LOGS_LIST, LB_RESETCONTENT, 0, 0);
}

//生成客户端密钥（skey）
//算法：MD5(用户名 + 当前服务器时间)，防止同一帐号登录的时候发生冲突
void CMyServer::CreateSKey(char *lpUsername, SYSTEMTIME tm, char *lpSKey)
{
	char lpTmp[128] = {0};
	sprintf_s(lpTmp, 128, "%s%u%u%u%u%u%u%u", lpUsername, tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	
	string str(lpTmp);
	MD5 md5(str);
	str = md5.toString();
	debug.Trace("生成的密钥是：%s\n", str.c_str());
	strncpy(lpSKey, str.c_str(), CLIENTKEY_LEN);
}


//处理接收到的命令
void CMyServer::DispatchCommand(SOCKET s, NET_CMD cmd)
{
	if (s == INVALID_SOCKET)
	{
		return;
	}

	//获取套接字信息
	sockaddr_in addr;
	int nAddrLen = sizeof(addr);
	::getpeername(s, (sockaddr*)&addr, &nAddrLen);

	/*查询版本号请求*/
	if (cmd.nCmd == CMD_QUERY_VERSION)
	{
		AddLog("客户机[%s:%d]正在验证版本%s...", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), cmd.szVersion);

		//正则表达式
		char szRegex[50] = {0};
		const regex rVersion("^[0-9]+(\\.[0-9]+)?$");

		//检测版本号合法性
		string strVersion(cmd.szVersion);
		if (!regex_match(strVersion, rVersion))
		{
			NET_CMD cmdMismatch;
			cmdMismatch.bAllow = false;
			cmdMismatch.nCmd = CMD_QUERY_VERSION;
			sprintf_s(cmdMismatch.szWarningText, MAX_WARNING_LEN, "版本号非法！");
			::send(s, (const char*)&cmdMismatch, sizeof(cmdMismatch), 0);
			AddLog("版本号[%s]不合法，拒绝登录。", cmd.szVersion);
			return;
		}

		//查询数据库，获取接收到的版本号是否允许登录
		SACommand SQLQueryVersion(&m_DbConn, "select * from sys_version where Version = :1;");
		SQLQueryVersion << cmd.szVersion;
		SQLQueryVersion.Execute();

		//判断版本号
		while (SQLQueryVersion.FetchNext())
		{
			if (strcmp((const char*)SQLQueryVersion.Field("Version").asString(), cmd.szVersion) == 0)
			{
				NET_CMD cmdQueryVersion;
				cmdQueryVersion.nCmd = CMD_QUERY_VERSION;

				//允许登录的情况下
				if (SQLQueryVersion.Field("AllowLogin").asBool())
				{
					AddLog("客户机[%s:%d]验证版本通过。", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
					cmdQueryVersion.bAllow = true;
				}
				else
				{
					//不允许该版本登录的情况下
					AddLog("客户机[%s:%d]版本已经停止登录，拒绝登录。", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
					sprintf_s(cmdQueryVersion.szWarningText, MAX_WARNING_LEN, "该版本已经停止登录，请更新到最新版本！");
					cmdQueryVersion.bAllow = false;
				}

				::send(s, (const char*)&cmdQueryVersion, sizeof(cmdQueryVersion), 0);
				return;
			}
		}

		m_DbConn.Commit();

		//客户端版本号异常（数据库内不存在该版本号的记录）
		NET_CMD cmdVersionError;
		cmdVersionError.nCmd = CMD_QUERY_VERSION;
		cmdVersionError.bAllow = false;
		sprintf_s(cmdVersionError.szWarningText, MAX_WARNING_LEN, "客户端版本号异常！");
		::send(s, (const char*)&cmdVersionError, sizeof(cmdVersionError), 0);
		AddLog("客户机[%s:%d]版本号出现异常，数据库不存在该版本记录", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
	}
	else if (cmd.nCmd == CMD_LOGIN)  /*登录请求*/
	{

		AddLog("用户[%s]正在登录...", cmd.szUsername);

		//正则表达式
		const regex rUsername(REGEX_USERNAME);
		const regex rPassword(REGEX_PASSWORD);
		string strUsername(cmd.szUsername);
		string strPassword(cmd.szPassword);

		//检测用户名是否合法
		if (!regex_match(strUsername, rUsername))
		{
			NET_CMD cmdMismatch;
			cmdMismatch.bAllow = false;
			cmdMismatch.nCmd = CMD_LOGIN;
			sprintf_s(cmdMismatch.szWarningText, MAX_WARNING_LEN, "用户名不合法，无法登录！");
			::send(s, (const char*)&cmdMismatch, sizeof(cmdMismatch), 0);
			AddLog("用户名[%s]不合法，拒绝登录。", cmd.szUsername);
			AddServerLog("用户名不合法", cmd.szUsername);
			return;
		}

		//检测密码是否为空
		if (strlen(cmd.szPassword) == 0)
		{
			NET_CMD cmdPasswordNull;
			cmdPasswordNull.bAllow = false;
			cmdPasswordNull.nCmd = CMD_LOGIN;
			sprintf_s(cmdPasswordNull.szWarningText, MAX_WARNING_LEN, "密码非法！");
			::send(s, (const char*)&cmdPasswordNull, sizeof(cmdPasswordNull), 0);
			AddLog("密码[%s]为空，拒绝登录。", cmd.szPassword);
			AddServerLog("密码为空", cmd.szUsername);
			return;
		}

		//检测机器码是否为空
		if (VERIFY_MACHINE_CODE == true)
		{
			if (strcmp(cmd.szMachineCode, "") == 0)
			{
				NET_CMD cmdMachineCodeNull;
				cmdMachineCodeNull.bAllow = false;
				cmdMachineCodeNull.nCmd = CMD_LOGIN;
				sprintf_s(cmdMachineCodeNull.szWarningText, MAX_WARNING_LEN, "机器码非法！");
				::send(s, (const char*)&cmdMachineCodeNull, sizeof(cmdMachineCodeNull), 0);
				AddLog("机器码为空，拒绝登录。", cmd.szMachineCode);
				AddServerLog("机器码为空", cmd.szUsername);
				return;
			}
		}

		//查询数据库
		SACommand SQLLogin(&m_DbConn, "select * from Members where Username = :1 and Password = :2;");
		SQLLogin << cmd.szUsername;
		SQLLogin << cmd.szPassword;
		SQLLogin.Execute();

		while (SQLLogin.FetchNext())
		{
			//校验用户名和密码是否正确
			if (strcmp(cmd.szUsername, (const char*)SQLLogin.Field("Username").asString()) == 0 
				&& strcmp(cmd.szPassword, (const char*)SQLLogin.Field("Password").asString()) == 0)
			{
				//验证机器码
				if (VERIFY_MACHINE_CODE == true)
				{
					if (strcmp(cmd.szMachineCode, (const char*)SQLLogin.Field("MachineCode").asString()) != 0)
					{
						NET_CMD cmdErrorMachineCode;
						cmdErrorMachineCode.bAllow = false;
						cmdErrorMachineCode.nCmd = CMD_LOGIN;
						sprintf_s(cmdErrorMachineCode.szWarningText, MAX_WARNING_LEN, "该帐号已经绑定了其它机器，无法在此机器登录！");
						::send(s, (const char*)&cmdErrorMachineCode, sizeof(cmdErrorMachineCode), 0);
						AddLog("用户[%s]机器码验证不通过，拒绝登录。", cmd.szUsername);
						AddServerLog("机器码已经绑定了其它机器", cmd.szUsername);
						return;
					}
				}

				//验证是否允许登录
				if (SQLLogin.Field("AllowLogin").asBool() == false)
				{
					NET_CMD cmdBanLogin;
					cmdBanLogin.nCmd = CMD_LOGIN;
					cmdBanLogin.bAllow = false;
					sprintf_s(cmdBanLogin.szWarningText, MAX_WARNING_LEN, "对不起，该帐号已经停止登录！");
					::send(s, (const char*)&cmdBanLogin, sizeof(cmdBanLogin), 0);
					AddLog("用户[%s]已被禁止登录。", cmd.szUsername);
					AddServerLog("用户已经被禁止登录", cmd.szUsername);
					return;
				}

				//检测余额
				AddLog("正在查询用户[%s]的使用时限...", cmd.szUsername);

				//判断是否已经激活
				if (SQLLogin.Field("Card_Kind").asString() == "未激活")
				{
					NET_CMD cmdNonActivated;
					cmdNonActivated.nCmd = CMD_LOGIN;
					cmdNonActivated.bAllow = false;

					sprintf_s(cmdNonActivated.szWarningText, MAX_WARNING_LEN, "对不起，您的账户尚未激活，请充值！");
					::send(s, (const char*)&cmdNonActivated, sizeof(cmdNonActivated), 0);
					AddLog("用户[%s]未激活，已拒绝登录。", cmd.szUsername);
					AddServerLog("账户未激活，拒绝登录", cmd.szUsername);
					return;
				}

				SADateTime dtEnd = SQLLogin.Field("Card_EndTime").asDateTime();
				CDealTime tmEnd = SAToDeal(dtEnd);
				CDealTime tmResult = tmEnd - CDealTime::GetLocalTime();

				debug.Trace("秒数：%d\n", tmResult.GetTime());

				//已经过期
				if (tmResult.GetTime() <= 0)
				{
					AddLog("用户[%s]的使用时间到期，已拒绝登录。", cmd.szUsername);
					AddServerLog("账户会员卡已经到期", cmd.szUsername);

					NET_CMD cmdTimePast;
					cmdTimePast.nCmd = CMD_LOGIN;
					cmdTimePast.bAllow = false;

					sprintf_s(cmdTimePast.szWarningText, MAX_WARNING_LEN, "对不起，您的会员卡[%s]已经过到期，请充值！", (const char*)SQLLogin.Field("Card_Kind").asString());
					::send(s, (const char*)&cmdTimePast, sizeof(cmdTimePast), 0);
					return;
				}
				else
				{
					AddLog("用户[%s]验证使用时限通过，成功登录。", cmd.szUsername);
					AddServerLog("成功登录", cmd.szUsername);

					NET_CMD cmdLoginSuccess;
					cmdLoginSuccess.nCmd = CMD_LOGIN;
					cmdLoginSuccess.bAllow = true;

					//生成ClientKey
					char szKey[CLIENTKEY_LEN] = {0};
					SYSTEMTIME tm;
					GetLocalTime(&tm);
					CreateSKey(cmd.szUsername, tm, szKey);
					strcpy_s(cmdLoginSuccess.szClientKey, CLIENTKEY_LEN, szKey);

					::send(s, (const char*)&cmdLoginSuccess, sizeof(cmdLoginSuccess), 0);

					//添加客户机信息结构到列表
					CLIENT_INFO Client_New;
					Client_New.s = s;
					Client_New.LoginTime = tm;
					strncpy(Client_New.username, cmd.szUsername, MAX_USERNAME_LEN);
					strncpy(Client_New.skey, szKey, CLIENTKEY_LEN);
					this->server.AddClient(Client_New);

					//更新在线列表
					UpdateOnlineList();
					return;
				}   // if (tmResult.GetTime() <= 0)
			}     // if (strcmp(cmd.szUsername, (const char*)sql.Field("Username").asString())...
		}       // while (sql.FetchNext())

		m_DbConn.Commit();

		//帐号不存在，帐号/密码错误
		AddLog("用户[%s]登录失败！帐号/密码错误。\n", cmd.szUsername, cmd.szPassword);
		AddServerLog("帐号/密码错误", cmd.szUsername);
		NET_CMD cmdLoginFailed;
		cmdLoginFailed.nCmd = CMD_LOGIN;
		cmdLoginFailed.bAllow = false;
		sprintf_s(cmdLoginFailed.szWarningText, MAX_WARNING_LEN, "帐号/密码错误！");
		::send(s, (const char*)&cmdLoginFailed, sizeof(cmdLoginFailed), 0);
		SQLLogin.Close();
		
	}//else if (cmd.nCmd == CMD_LOGIN)


	/*充值请求*/
	else if (cmd.nCmd == CMD_RECHARGE)
	{
		AddLog("正在为用户 [%s] 充值...", cmd.szUsername);

		//检测卡号和密码是否为空
		if (strlen(cmd.szCardId) == 0 || strlen(cmd.szCardPass) == 0)
		{
			NET_CMD cmdCardNull;
			cmdCardNull.bAllow = false;
			cmdCardNull.nCmd = CMD_RECHARGE;
			sprintf_s(cmdCardNull.szWarningText, MAX_WARNING_LEN, "充值卡卡号/密码不能为空！");
			::send(s, (char*)&cmdCardNull, sizeof(cmdCardNull), 0);
			AddLog("用户[%s]充值卡卡号/密码为空！", cmd.szUsername);
			AddServerLog("充值卡卡号/密码为空", cmd.szUsername);
			return;
		}

		//检测数据库内是否存在该卡号记录
		SACommand SQLVerifyCard(&m_DbConn, "select * from Card_Recharge where CardId = :1 and CardPassword = :2");
		SQLVerifyCard << cmd.szCardId;
		SQLVerifyCard << cmd.szCardPass;
		SQLVerifyCard.Execute();

		while (SQLVerifyCard.FetchNext())
		{

			//检测充值卡状态
			SACommand SQLQueryCardState(&m_DbConn, "select * from Card_Recharge where CardId = :1;");
			SQLQueryCardState << cmd.szCardId;
			SQLQueryCardState.Execute();

			while (SQLQueryCardState.FetchNext())
			{
				if (SQLQueryCardState.Field("IsUsed").asBool() == true)
				{
					NET_CMD cmdErrorRecharge;
					cmdErrorRecharge.bAllow = false;
					cmdErrorRecharge.nCmd = CMD_RECHARGE;
					sprintf_s(cmdErrorRecharge.szWarningText, MAX_WARNING_LEN, "该充值卡已不能使用！");
					::send(s, (char*)&cmdErrorRecharge, sizeof(cmdErrorRecharge), 0);
					AddLog("充值卡[%s]已不能使用！", cmd.szCardId);
					AddServerLog("充值卡已不能使用", cmd.szUsername);
					return;
				}
			}

			//充值卡有效期
			int nCardLife = SQLQueryCardState.Field("CardLife").asLong();
			CDealTime tmCardLife(nCardLife);

			//取得充值卡信息
			SAString strCardKind = SQLVerifyCard.Field("CardKind").asString();	//充值卡类型

			//为用户充值
			SACommand SQLRecharge(&m_DbConn, 
				"UPDATE Members SET Username = :1, Card_Kind = :2, Card_StartTime = :3, Card_EndTime = :4, MachineCode = :5 WHERE Username = :6");
			SQLRecharge << cmd.szUsername;
			SQLRecharge << strCardKind;
			SQLRecharge << SADateTime::currentDateTime();

			//计算用户的到期时间
			CDealTime tmNowTime(CDealTime::GetLocalTime());
			CDealTime tmEndTime(tmNowTime.GetTime() + tmCardLife.GetTime());

			//用户的到期时间
			SADateTime dtEndTime = DealToSA(tmEndTime);
			SQLRecharge << dtEndTime;
			
			//绑定新的机器码
			SQLRecharge << cmd.szMachineCode;

			//条件
			SQLRecharge << cmd.szUsername;

			//执行充值操作
			SQLRecharge.Execute();

			//判断是否失败
			if (SQLRecharge.RowsAffected() == 0)
			{
				NET_CMD cmdErrorRecharge;
				cmdErrorRecharge.bAllow = false;
				cmdErrorRecharge.nCmd = CMD_RECHARGE;
				sprintf_s(cmdErrorRecharge.szWarningText, MAX_WARNING_LEN, "充值失败，该用户可能不存在！");
				::send(s, (char*)&cmdErrorRecharge, sizeof(cmdErrorRecharge), 0);
				AddLog("用户[%s]充值失败！", cmd.szUsername);
				AddServerLog("充值卡失败，未知错误", cmd.szUsername);
				return;
			}

			//改变充值卡状态
			SACommand SQLChageCardState(&m_DbConn, 
				"UPDATE Card_Recharge SET IsUsed = 1,RechargeUser = :1, RechargeDate = :2, BindMachineCode = :3");// ,
			SQLChageCardState << cmd.szUsername;
			SQLChageCardState << DealToSA(tmNowTime);
			SQLChageCardState << cmd.szMachineCode;
			SQLChageCardState.Execute();

			if (SQLChageCardState.RowsAffected() > 0)
			{
				NET_CMD cmdRechargeSuccess;
				cmdRechargeSuccess.bAllow = true;
				cmdRechargeSuccess.nCmd = CMD_RECHARGE;
				::send(s, (char*)&cmdRechargeSuccess, sizeof(cmdRechargeSuccess), 0);

				//计算有效天数
				//SADateTime dtUseful = dtCardLife - dtNow;
				//CDealTime tmUseful(nCardLife);

				AddLog("用户[%s]成功充值了一张[%s]:", cmd.szUsername, strCardKind);

				AddLog("    >> 到期时间：%d年%d月%d日%d时%d分%d秒",
					dtEndTime.GetYear(),
					dtEndTime.GetMonth(),
					dtEndTime.GetDay(),
					dtEndTime.GetHour(),
					dtEndTime.GetMinute(),
					dtEndTime.GetSecond()
					);

				AddLog("    >> 有效期为：%d天%d小时%d分.",
					tmCardLife.GetDay() - 1,
					tmCardLife.GetHour() - 8,
					tmCardLife.GetMinute()
					);

				AddServerLog("用户充值成功", cmd.szUsername);
			}

			m_DbConn.Commit();
			return;
		}//while (SQLVerifyCard.FetchNext())

		NET_CMD cmdErrorRecharge;
		cmdErrorRecharge.bAllow = false;
		cmdErrorRecharge.nCmd = CMD_RECHARGE;
		sprintf_s(cmdErrorRecharge.szWarningText, MAX_WARNING_LEN, "充值卡卡号/密码错误！");
		::send(s, (char*)&cmdErrorRecharge, sizeof(cmdErrorRecharge), 0);
		AddLog("用户[%s]充值卡卡号/密码错误！", cmd.szUsername);
		AddServerLog("充值卡卡号/密码错误", cmd.szUsername);
		return;

		m_DbConn.Commit();

	}//else if (cmd.nCmd == CMD_RECHARGE)

	/*注册请求*  注册内容：用户名、密码、QQ、注册IP、注册时间*/
	else if (cmd.nCmd == CMD_REGISTER)
	{
		AddLog("[%s] 正在注册...", cmd.szUsername);

		//检测用户名是否已经存在
		SACommand SQLCheckExist(&m_DbConn, "select * from Members where username = :1");
		SQLCheckExist << cmd.szUsername;
		SQLCheckExist.Execute();

		while (SQLCheckExist.FetchNext())
		{
			NET_CMD cmdUserExist;
			cmdUserExist.bAllow = false;
			cmdUserExist.nCmd = CMD_REGISTER;
			sprintf_s(cmdUserExist.szWarningText, MAX_WARNING_LEN, "用户名已经存在。");
			::send(s, (char*)&cmdUserExist, sizeof(cmdUserExist), 0);
			AddLog("用户名[%s]已经存在，注册请求拒绝。", cmd.szUsername);
			return;
		}

		m_DbConn.Commit();

		//执行SQL，添加记录
		SACommand SQLRegister(&m_DbConn, 
			"insert into Members(Username, Password, QQNumber, RegisterIP, RegisterTime, MachineCode, Card_StartTime, Card_EndTime, Card_Kind, AllowLogin)VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10);"
			);

		SADateTime dtNow = SADateTime::currentDateTime();
		SADateTime dtDefault(2000, 1, 1, 0, 0, 0);
		
		SQLRegister << cmd.szUsername;
		SQLRegister << cmd.szPassword;
		SQLRegister << cmd.szQQNumber;
		SQLRegister << inet_ntoa(addr.sin_addr);
		SQLRegister << dtNow;
		SQLRegister << "";			//机器码初始为空
		SQLRegister << dtDefault;
		SQLRegister << dtDefault;
		SQLRegister << "未激活";
		SQLRegister << true;

		SQLRegister.Execute();
		
		if (SQLRegister.RowsAffected() > 0)
		{
			NET_CMD cmdRegisterSuccess;
			cmdRegisterSuccess.bAllow = true;
			cmdRegisterSuccess.nCmd = CMD_REGISTER;
			sprintf_s(cmdRegisterSuccess.szWarningText, MAX_WARNING_LEN, "注册成功！");
			::send(s, (char*)&cmdRegisterSuccess, sizeof(cmdRegisterSuccess), 0);
			AddLog("用户[%s]注册成功。", cmd.szUsername);
			AddServerLog("注册成功", cmd.szUsername);
		}

		m_DbConn.Commit();

		return;
	}//else if (cmd.nCmd == CMD_REGISTER)



	/*心跳包请求*/
	else if (cmd.nCmd == CMD_C_HEART_PACKET)
	{
		for (list<CLIENT_INFO>::iterator iter = server.m_ClientList.begin(); 
			iter != server.m_ClientList.end(); iter++)
		{
			if (iter->s == s)
			{
				//返回结构指针
				iter->nHeartPacketTime = HEART_TIMEOUT;
				break;
			}
		}
	}//else if (cmd.nCmd == CMD_C_HEART_PACKET)


	/*查询到期时间*/
	else if (cmd.nCmd == CMD_QUERY_MEMBER)
	{
		AddLog("正在查询用户 [%s] 的到期时间...", cmd.szUsername);
		SACommand SQLQueryTimeOut(&m_DbConn, "SELECT Card_StartTime, Card_EndTime, Card_Kind FROM Members WHERE Username = :1;");
		SQLQueryTimeOut << cmd.szUsername;
		SQLQueryTimeOut.Execute();

		while (SQLQueryTimeOut.FetchNext())
		{
			//获取会员卡开始时间和到期时间
			CDealTime tmEnd = SAToDeal(SQLQueryTimeOut.Field("Card_EndTime").asDateTime());
			CDealTime tmNow = CDealTime::GetLocalTime();

			time_t t1, t2;
			t1 = tmEnd.GetTime();
			t2 = tmNow.GetTime();
			CDealTime tmRemain = t1 - t2;

			//获取会员卡类型
			SAString strCardKind = SQLQueryTimeOut.Field("Card_Kind").asString();

			//判断是否已经到期
			if (tmRemain.GetTime() <= 0)
			{
				NET_CMD cmdQueryFailed;
				cmdQueryFailed.bAllow = false;
				cmdQueryFailed.nCmd = CMD_QUERY_MEMBER;

				cmdQueryFailed.tmDateTimeOut.Years = tmEnd.GetYear();
				cmdQueryFailed.tmDateTimeOut.Months = tmEnd.GetMonth();
				cmdQueryFailed.tmDateTimeOut.Days = tmEnd.GetDay();
				cmdQueryFailed.tmDateTimeOut.Hours = tmEnd.GetHour();
				cmdQueryFailed.tmDateTimeOut.Minutes = tmEnd.GetMinute();
				cmdQueryFailed.tmDateTimeOut.Seconds = tmEnd.GetSecond();

				cmdQueryFailed.tmRemain.Years = 0;
				cmdQueryFailed.tmRemain.Months = 0;
				cmdQueryFailed.tmRemain.Days = 0;
				cmdQueryFailed.tmRemain.Hours = 0;
				cmdQueryFailed.tmRemain.Minutes = 0;
				cmdQueryFailed.tmRemain.Seconds = 0;

				strncpy(cmdQueryFailed.szCardKind, strCardKind, CARD_KIND_LEN);
				sprintf_s(cmdQueryFailed.szWarningText, MAX_WARNING_LEN, "会员卡已经到期。");
				AddLog("用户 [%s] 时间查询完成，会员卡已过期", cmd.szUsername);
				::send(s, (char*)&cmdQueryFailed, sizeof(cmdQueryFailed), 0);

				return;
			}

			NET_CMD cmdQueryTimeOut;
			cmdQueryTimeOut.bAllow = true;
			cmdQueryTimeOut.nCmd = CMD_QUERY_MEMBER;

			cmdQueryTimeOut.tmDateTimeOut.Years = tmEnd.GetYear();
			cmdQueryTimeOut.tmDateTimeOut.Months = tmEnd.GetMonth();
			cmdQueryTimeOut.tmDateTimeOut.Days = tmEnd.GetDay();
			cmdQueryTimeOut.tmDateTimeOut.Hours = tmEnd.GetHour();
			cmdQueryTimeOut.tmDateTimeOut.Minutes = tmEnd.GetMinute();
			cmdQueryTimeOut.tmDateTimeOut.Seconds = tmEnd.GetSecond();

			cmdQueryTimeOut.tmRemain.Years = tmRemain.GetYear() - 1970;
			cmdQueryTimeOut.tmRemain.Months = tmRemain.GetMonth() - 1;
			cmdQueryTimeOut.tmRemain.Days = tmRemain.GetDay() - 1;
			cmdQueryTimeOut.tmRemain.Hours = tmRemain.GetHour() - 8;
			cmdQueryTimeOut.tmRemain.Minutes = tmRemain.GetMinute();
			cmdQueryTimeOut.tmRemain.Seconds = tmRemain.GetSecond();

			strncpy(cmdQueryTimeOut.szCardKind, strCardKind, CARD_KIND_LEN);

			AddLog("用户 [%s] 时间查询完成", cmd.szUsername);
			AddLog("    >> 会员卡类型：%s", cmdQueryTimeOut.szCardKind);
			AddLog("    >> 到期时间：%d年%d月%d日%d时%d分%d秒",
				cmdQueryTimeOut.tmDateTimeOut.Years,
				cmdQueryTimeOut.tmDateTimeOut.Months,
				cmdQueryTimeOut.tmDateTimeOut.Days,
				cmdQueryTimeOut.tmDateTimeOut.Hours,
				cmdQueryTimeOut.tmDateTimeOut.Minutes,
				cmdQueryTimeOut.tmDateTimeOut.Seconds
				);

			AddLog("    >> 有效期为：%d天%d小时%d分%d秒.",
				cmdQueryTimeOut.tmRemain.Days,
				cmdQueryTimeOut.tmRemain.Hours,
				cmdQueryTimeOut.tmRemain.Minutes,
				cmdQueryTimeOut.tmRemain.Seconds
				);

			sprintf_s(cmdQueryTimeOut.szWarningText, MAX_WARNING_LEN, "查询完成。");
			::send(s, (char*)&cmdQueryTimeOut, sizeof(cmdQueryTimeOut), 0);
		}

	}
}

//更新在线列表
void CMyServer::UpdateOnlineList()
{
	if (server.m_ClientList.empty())
	{
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_DELETEALLITEMS, 0, 0);
		return;
	}

	SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_DELETEALLITEMS, 0, 0);

	//计数
	int i = 0;

	for (list<CLIENT_INFO>::iterator iter = server.m_ClientList.begin(); 
		iter != server.m_ClientList.end(); 
		iter++
		)
	{
		
		LVITEMA lvi = {0};
		lvi.mask = LVIF_TEXT;
		lvi.iItem = i;
		
		//添加用户名
		lvi.iSubItem = 0;
		lvi.pszText = iter->username;
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_INSERTITEMA, NULL, (LPARAM)&lvi);

		//添加登录时间
		char szLoginTime[48] = {0};
		sprintf(szLoginTime, "%u-%u-%u %u:%u:%u", 
			iter->LoginTime.wYear,
			iter->LoginTime.wMonth,
			iter->LoginTime.wDay,
			iter->LoginTime.wHour,
			iter->LoginTime.wMinute,
			iter->LoginTime.wSecond
			);

		lvi.iSubItem = 1;
		lvi.pszText = szLoginTime;
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_SETITEMA, 0, (LPARAM)&lvi);

		//IP地址和端口
		lvi.iSubItem = 2;
		sockaddr_in addr;
		int nAddrLen = sizeof(addr);
		::getpeername(iter->s, (sockaddr*)&addr, &nAddrLen);
		
		char szIp[24] = {0};
		sprintf(szIp, "%s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
		lvi.pszText = szIp;
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_SETITEMA, 0, (LPARAM)&lvi);


		//地理位置
/*
		if (m_Locator.LoadLib("qqwry.dat") == false)
		{
			AddLog("加载IP数据库失败。");
			this->server.Stop();
			//return FALSE;
		}*/
/*
		lvi.iSubItem = 3;
		IpLocation location = m_Locator.GetIpLocation(m_Locator.IpStringToInt(inet_ntoa(addr.sin_addr)));
		debug.Trace("%d\n", m_Locator.IpStringToInt(inet_ntoa(addr.sin_addr)));
		char szLocator[255] = {0};
		sprintf(szLocator, "%s%s", location.Country, location.City);
		lvi.pszText = szLocator;
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_SETITEMA, 0, (LPARAM)&lvi);
		m_Locator.~CIPLocator();*/

		//心跳时间
		lvi.iSubItem = 3;
		itoa(iter->nHeartPacketTime, lvi.pszText, 10);
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_SETITEMA, 0, (LPARAM)&lvi);

		//添加ClientKey
		lvi.iSubItem = 4;
		lvi.pszText = iter->skey;
		SendDlgItemMessageA(this->m_hWnd, IDC_ONLINE_LIST, LVM_SETITEMA, 0, (LPARAM)&lvi);

		i += 1;
	}
}

//转换SADateTime到CDealTime
CDealTime CMyServer::SAToDeal(SADateTime &t) const
{
	CDealTime deal(t.GetYear(), t.GetMonth(), t.GetDay(), t.GetHour(), t.GetMinute(), t.GetSecond());
	return deal;
}

//转换CDealTime到SADateTime
SADateTime CMyServer::DealToSA(CDealTime &t) const
{
	SADateTime dt(t.GetYear(), t.GetMonth(), t.GetDay(), t.GetHour(), t.GetMinute(), t.GetSecond());
	return dt;
}

//添加服务器日志
void CMyServer::AddServerLog(char *lpText, char *lpUsername)
{
	SACommand SQLAddLog(&m_DbConn, "INSERT INTO sys_logs (LogTime, LogText, Username) VALUES (:1, :2, :3);");
	SQLAddLog << SADateTime::currentDateTime();
	SQLAddLog << lpText;
	SQLAddLog << lpUsername;
	SQLAddLog.Execute();

	if (SQLAddLog.RowsAffected() == 0)
	{
		return;
	}

	m_DbConn.Commit();
}